#pragma once

#include <iostream>
#include <vector>
#include <pthread.h>
#include <functional>
#include <semaphore.h>
#include <unistd.h>

const int N = 5;

template<class T>
class ringqueue
{
private:
    void lock(pthread_mutex_t& mutex)
    {
        pthread_mutex_lock(&mutex);
    }

    void unlock(pthread_mutex_t& mutex)
    {
        pthread_mutex_unlock(&mutex);
    }

    void P(sem_t& sem)
    {
        sem_wait(&sem);
    }

    void V(sem_t& sem)
    {
        sem_post(&sem);
    }
public:
    ringqueue(int cap = N)
        : _ring(cap)
        , _cap(cap)
    {
        sem_init(&_data_sem, 0, 0);
        sem_init(&_room_sem, 0, cap);
        _data_step = _room_step = 0;

        pthread_mutex_init(&_c_mutex, nullptr);
        pthread_mutex_init(&_p_mutex, nullptr);
    }

    void push(const T& in)
    {
        // 信号量的优点：可以不用在临界区内判断，就可以知道资源的可用情况
        P(_room_sem);
        lock(_p_mutex); // 先等待资源，可以保证必须进入临界区必然有可用资源

        _ring[_room_step++] = in;
        _room_step %= _cap; // 维护环形队列

        unlock(_p_mutex);
        V(_data_sem);
    }

    void pop(T* out)
    {
        P(_data_sem);
        lock(_c_mutex);

        *out = _ring[_data_step++];
        _data_step %= _cap;

        unlock(_c_mutex);
        V(_room_sem);
    }

    ~ringqueue()
    {
        sem_destroy(&_data_sem);
        sem_destroy(&_room_sem);

        pthread_mutex_destroy(&_c_mutex);
        pthread_mutex_destroy(&_p_mutex);
    }
private:
    std::vector<T> _ring;
    int _cap;
    
    sem_t _data_sem; // 只有消费者关心
    sem_t _room_sem; // 只有生产者关心

    int _data_step; // 消费位置
    int _room_step; // 生产位置

    pthread_mutex_t _c_mutex;
    pthread_mutex_t _p_mutex;
};

class Task
{
public:
    Task(int n = 0)
        : _n(n)
    {
        _tasks.push_back([]()
        { 
            std::cout << "执行网络任务..." << std::endl;
            usleep(2000);
        });

        _tasks.push_back([]()
        { 
            std::cout << "执行缓存任务..." << std::endl;
            usleep(2000);
        });

        _tasks.push_back([]()
        { 
            std::cout << "执行数据库任务..." << std::endl;
            usleep(2000);
        });

        _tasks.push_back([]()
        { 
            std::cout << "执行IO任务..." << std::endl;
            usleep(2000);
        });
    }

    void run()
    {
        _tasks[_n]();
    }

    int cap()
    {
        return _cap;
    }
private:
    std::vector<std::function<void()>> _tasks;
    int _n;
    int _cap = 4;
};